package sf.dsl;

import sf.core.DBField;
import sf.database.OrmContext;
import sf.database.OrmParameter;
import sf.database.dao.DBContext;
import sf.database.dialect.DBDialect;
import sf.database.meta.ColumnMapping;
import sf.database.util.OrmUtils;
import sf.database.util.SimpleSQLTemplate;
import sf.spring.util.CollectionUtils;
import sf.tools.StringUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class ExampleSQLHelp {
    public static String getColumn(Example example, boolean useAlias) {
        StringBuilder sb = new StringBuilder();
        if (CollectionUtils.isNotEmpty(example.getSelectColumns())) {
            Map<DBField, ColumnMapping> schemaMap = example.table.getSchemaMap();
            boolean f = false;
            for (DBField field : example.getSelectColumns()) {
                sb.append(f ? "," : "").append(example.getColumnAlias(example.dialect.wrapKeyword(schemaMap.get(field).rawColumnName), useAlias));
                f = true;
            }
        }
//        else if(example.countColumn!=null){
//            Map<DBField, ColumnMapping> schemaMap = example.table.getSchemaMap();
//            sb.append(" count(").append(example.dialect.wrapKeyword(schemaMap.get(example.countColumn).rawColumnName)).append(") ");
//        }
        return sb.toString();
    }

    public static String getCountColumn(Example example, boolean useAlias) {
        StringBuilder sb = new StringBuilder();
        if (example.countColumn != null) {
            Map<DBField, ColumnMapping> schemaMap = example.table.getSchemaMap();
            sb.append(" count(").append(example.getColumnAlias(example.dialect.wrapKeyword(schemaMap.get(example.countColumn).rawColumnName), useAlias)).append(") ");
        }
        return sb.toString();
    }

    /**
     * 获取完整的查询
     * @param example
     * @param context
     * @param count       是否查询count
     * @param offsetLimit 是否使用offsetLimit
     * @param useAlias    是否使用别名
     * @return
     */
    public static OrmContext getSelectOrmContext(Example example, DBContext context, boolean count, boolean offsetLimit, boolean useAlias) {
        String selectCaluse = getSelectClause(example, count, useAlias);
        String fromCaluse = getFromClause(example, context, useAlias);
        OrmContext ormContext = getOrmContextWhereClause(example, useAlias);
        String orderByCaluse = getOrderByClause(example, useAlias);
        StringBuilder sql = new StringBuilder();
        sql.append(selectCaluse).append(" from ").append(fromCaluse);
        if (StringUtils.isNotBlank(ormContext.getSql())) {
            sql.append(" where ").append(ormContext.getSql());
        }
        if (StringUtils.isNotBlank(orderByCaluse)) {
            sql.append(" ").append(orderByCaluse);
        }
        if (offsetLimit && example.getOffsetLimit() != null) {
            ormContext.setSql(example.dialect.sqlPageList(sql.toString(), example.getOffsetLimit().getOffset(), example.getOffsetLimit().getLimit()));
        } else {
            ormContext.setSql(sql.toString());
        }
        return ormContext;
    }

    public static String getSelectClause(Example example, boolean count, boolean useAlias) {
        if (count) {
            if (example.getCountColumn() != null) {
                return "select " + getCountColumn(example, useAlias);
            } else {
                return "select count(1) ";
            }
        }
        Set<DBField> selectColumns = example.getSelectColumns();
        if (CollectionUtils.isNotEmpty(selectColumns) || StringUtils.isNotBlank(example.select)) {
            StringBuilder selectSql = new StringBuilder();
            selectSql.append("select ");
            if (example.isDistinct()){
                selectSql.append("distinct ");
            }
            if (CollectionUtils.isNotEmpty(selectColumns)) {
                selectSql.append(getColumn(example, useAlias));
            }
            if (StringUtils.isNotBlank(example.select)) {
                selectSql.append(CollectionUtils.isNotEmpty(selectColumns) ? "," : "").append(SimpleSQLTemplate.unwrapper(example.dialect,example.select));
            }
            return selectSql.toString();
        }
        return "select " + example.getColumnAlias("*", useAlias);
    }

    public static String getFromClause(Example example, DBContext context, boolean useAlias) {
        String tableName = example.tableName;
        String from = example.from;
        if (from == null) {
            from = "";
        } else {
            from = " " + from;
        }
        if (StringUtils.isBlank(tableName)) {
            tableName = OrmUtils.getDynamicTableName(context, example.table);
        }
        if (example.dialect != null) {
            return example.getTableNameAlias(example.dialect.wrapKeyword(tableName), useAlias) + from;
        }
        return example.getTableNameAlias(tableName, useAlias) + from;
    }

    public static OrmContext getOrmContextWhereClause(Example example, boolean useAlias) {
        OrmContext context = new OrmContext();
        List<Example.Criteria> criteriaList = example.criterias;
        StringBuilder sb = new StringBuilder();
        List<OrmParameter> parameters = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(criteriaList)) {
            boolean flag = false;
            for (Example.Criteria criteria : criteriaList) {
                if (criteria.isValid() && CollectionUtils.isNotEmpty(criteria.criteria)) {
                    if (flag) {
                        sb.append(" ").append(criteria.andOr).append(" (");
                    }
                    for (Example.Criterion criterion : criteria.criteria) {
                        switch (criterion.getValueType()) {
                            case noValue:
                                sb.append(getSqlFragment(example, criterion, useAlias));
                                break;
                            case singleValue:
                                sb.append(getSqlFragment(example, criterion, useAlias));
                                parameters.add(new OrmParameter(criterion.getValue(), criterion.getColumn()));
                                break;
                            case betweenValue:
                                sb.append(getSqlFragment(example, criterion, useAlias));
                                Iterator<?> it = ((Collection<?>)criterion.getValue()).iterator();
                                parameters.add(new OrmParameter(it.next(), criterion.getColumn()));
                                parameters.add(new OrmParameter(it.next(), criterion.getColumn()));
                                break;
                            case listValue:
                                sb.append(getSqlFragment(example, criterion, useAlias));
                                for (Object obj : (Collection<?>)criterion.getValue()) {
                                    parameters.add(new OrmParameter(obj, criterion.getColumn()));
                                }
                                break;
                            default:
                                break;
                        }
                    }
                    if (flag) {
                        sb.append(")");
                    }
                    flag = true;
                }
            }
        }
        context.setSql(sb.toString());
        context.setParas(parameters);
        return context;
    }

    private static String getSqlFragment(Example example, Example.Criterion criteria, boolean useAlias) {
        DBDialect dbDialect = example.dialect;
        if (dbDialect != null && criteria.getColumn() != null) {
            return example.getColumnAlias(dbDialect.wrapKeyword(criteria.getColumn().rawColumnName), useAlias) + criteria.getCondition();
        }
        if (criteria.getColumn() != null) {
            return example.getColumnAlias(criteria.getColumn().rawColumnName, useAlias) + criteria.getCondition();
        }
        return criteria.getCondition();
    }

    public static String getOrderByClause(Example example, boolean useAlias) {
        String orderBySql = example.getOrderByClause(useAlias);
        if (orderBySql != null && orderBySql.length() > 0) {
            return " order by " + orderBySql;
        }
        return "";
    }
}
